package com.newly.center.sys.service.auth.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import com.newly.center.sys.service.auth.api.IAuthService;
import com.newly.center.sys.service.authEntity.api.IAuthEntityService;
import com.newly.center.sys.service.loginRecord.api.ILoginRecordService;
import com.newly.center.sys.service.role.api.IRoleService;
import com.newly.common.base.constants.AuthConstant;
import com.newly.common.base.entity.auth.AuthPayload;
import com.newly.common.base.entity.auth.ICurrentTenant;
import com.newly.common.base.entity.auth.TenantDetails;
import com.newly.common.base.entity.sys.api.ApiDto;
import com.newly.common.base.entity.sys.api.ApiVo;
import com.newly.common.base.entity.sys.auth.entity.po.AuthEntityPo;
import com.newly.common.base.entity.sys.auth.role.po.RolePo;
import com.newly.common.base.entity.sys.captcha.dto.CaptchaDto;
import com.newly.common.base.entity.sys.captcha.vo.CaptchaVo;
import com.newly.common.base.exception.NewlyAuthException;
import com.newly.common.base.exception.NewlyGlobalException;
import com.newly.common.core.service.core.api.ICoreService;
import com.newly.common.core.utils.CaptchaUtil;
import com.newly.transfer.redis.RedisTransfer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author gjm guan
 * @date 2021/9/12 10:28
 * @desc 鉴权业务实现
 */
@Service
@Slf4j
public class AuthService extends AbstractAuthService implements IAuthService {


    @Autowired
    private IRoleService roleService;

    @Autowired
    private ICoreService coreService;

    @Autowired
    private IAuthEntityService authEntityService;

    @Autowired
    private ILoginRecordService loginRecordService;


    @Override
    public boolean logout() {
//        ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
//        redisTransfer.clearTenantDetails(currentTenant.getUsername());
        boolean result = loginRecordService.modifyEndTime();
        return true;
    }

    @Override
    public CaptchaVo generateCaptcha() {
        // 生成图形验证码数据
        CaptchaUtil.Validate randomCode = CaptchaUtil.getRandomCode();
        String base64Str = randomCode.getBase64Str(); // base64验证码
        String code = randomCode.getValue(); // code码

        String captchaId = UUID.randomUUID().toString();

        // 生成图形验证码的redis存储数据
        CaptchaDto captchaDto = new CaptchaDto();
        captchaDto.setCaptchaId(captchaId);
        captchaDto.setCode(code);
        captchaDto.setExpireTime(RedisTransfer.RedisSetting.CAPTCHA.getTime());

        // 存储至redis
        boolean flag = redisTransfer.setCaptcha(captchaId, captchaDto);

        if (!flag) throw new NewlyGlobalException("图形验证码生成失败");

        log.debug("captchaDto = {}", captchaDto);

        // 给客户端返回验证码数据
        CaptchaVo captchaVo = new CaptchaVo();
        captchaVo.setId(captchaId);
        captchaVo.setData(base64Str);
        captchaVo.setUri(code);
        captchaVo.setExpireTime(captchaDto.getExpireTime());
        return captchaVo;
    }

    @Override
    public boolean checkCaptcha(String captchaId, String code) {
        Assert.notBlank(captchaId, "captchaId不能为空！");
        Assert.notBlank(code, "code不能为空！");

        // 从redis中获取图形验证码
        CaptchaDto captchaDto = null;
        try {
            captchaDto = redisTransfer.getCaptcha(captchaId);
            Assert.notNull(captchaDto);
        } catch (Exception e) {
            throw new NewlyGlobalException("已过期");
        }

        // 比较验证码，校验成功后移除redis中的验证码对象
        if (code.equalsIgnoreCase(captchaDto.getCode())) {
            try {
                redisTransfer.clearCaptcha(captchaId);
            } catch (Exception e) {
                log.error("clear the captcha error: captchaDto = {}", captchaDto);
            }
            return true;
        }
        return false;
    }


    @Override
    public List<ApiVo> getApis() {
        List<ApiDto> cacheApis = coreService.getCacheApis();
        if (ObjectUtil.isEmpty(cacheApis)) return null;

        List<ApiVo> apiVos = BeanUtil.copyToList(cacheApis, ApiVo.class);
        return apiVos;
    }

    @Override
    public List<AuthPayload> getAuthPayloads(Long tenantId) throws NewlyAuthException {
        List<AuthPayload> payloads = new ArrayList<>();
        try {
            List<RolePo> rolePos = roleService.getByTenantId(tenantId);
            Assert.notEmpty(rolePos, "The current user has no role");
            for (RolePo rolePo : rolePos) {
                rolePo.setCurrentFlag(true);
            }
            List<RolePo> children = new ArrayList<>();
            roleService.getChildren(roleService.parseIds(rolePos), children);
            if (!ObjectUtil.isEmpty(children)) {
                rolePos.addAll(children);
            }
            for (RolePo rolePo : rolePos) {
                AuthPayload payload = new AuthPayload(rolePo);
                payloads.add(payload);
                List<AuthEntityPo> authEntityPos =
                        authEntityService.getByRoleId(rolePo.getId());
                if (ObjectUtil.isEmpty(authEntityPos)) {
                    log.error("The current role has no auth-entity");
                    continue;
                } else {
                    Map<AuthConstant.AuthEntityType,
                            List<AuthPayload.AuthEntity>> authEntities = new HashMap<>();
                    AuthConstant.AuthEntityType[] values = AuthConstant.AuthEntityType.values();
                    for (AuthConstant.AuthEntityType value : values) {
                        List<AuthPayload.AuthEntity> authEntity = new ArrayList<>();
                        authEntities.put(value, authEntity);
                        for (AuthEntityPo authEntityPo : authEntityPos) {
                            if (authEntityPo.getType().equals(value.getCode())) {
                                authEntity.add(new AuthPayload.AuthEntity(authEntityPo));
                            }
                        }
                    }
                    payload.setAuthEntities(authEntities);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new NewlyAuthException(e.getMessage());
        }
        return payloads;
    }

    @Override
    public boolean flushAuthPayloads() {
        boolean result = false;
        try {
            ICurrentTenant currentTenant = requestUtils.getCurrentTenant();
            log.debug("Flush auth-payload of current user, ID = {}", currentTenant.getUserId());
            List<AuthPayload> authPayloads = this.getAuthPayloads(currentTenant.getUserId());
            result = redisTransfer.setTenantDetails(currentTenant.getUsername(),
                    new TenantDetails(currentTenant, authPayloads));
        } catch (NewlyAuthException e) {
            e.printStackTrace();
            log.error("Flush auth-payload error");
        }
        return result;
    }

    @Override
    public boolean pushAuthPayloads(AuthConstant.AuthEntityType entityType, String entityId) {
        return pushAuthPayloads(entityType, Arrays.asList(entityId));
    }

    @Override
    public boolean pushAuthPayloads(AuthConstant.AuthEntityType entityType, List<String> entityIds) {
        return handleAuthPayloads(ADD, entityType, entityIds);
    }

    @Override
    public boolean popAuthPayloads(AuthConstant.AuthEntityType entityType, List<String> entityIds) {
        return handleAuthPayloads(DEL, entityType, entityIds);
    }

    @Override
    public boolean popAuthPayloads(AuthConstant.AuthEntityType entityType, String entityId) {
        return popAuthPayloads(entityType, Arrays.asList(entityId));
    }

}
